package com.fxu.tpl.api;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fxu.framework.biz.ano.Logger;
import com.fxu.framework.biz.ano.PermAuth;
import com.fxu.framework.biz.ano.SqlLogger;
import com.fxu.framework.biz.util.ErrorUtil;
import com.fxu.framework.core.base.Result;
import com.fxu.framework.core.dto.DetailReq;
import com.fxu.framework.core.dto.OffsetReq;
import com.fxu.framework.core.dto.PageReq;
import com.fxu.framework.core.enums.EnumValid;
import com.fxu.framework.core.enums.StatusEnum;
import com.fxu.framework.core.lock.LockHolder;
import com.fxu.framework.core.service.impl.ApiServiceImpl;
import com.fxu.framework.core.sql.JoinType;
import com.fxu.framework.core.sql.SQuery;
import com.fxu.framework.core.sql.SType;
import com.fxu.framework.core.sql.SUtils;
import com.fxu.tpl.entity.Test;
import com.fxu.tpl.entity.TestInfo;
import com.fxu.tpl.entity.TestItem;
import com.fxu.tpl.mapper.TestItemMapper;
import com.fxu.tpl.mapper.TestMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/***
 * <p>fxu</p>
 * 测试API接口
 * @author fangxu
 * @version 2021-03-13
 ***/
@Slf4j
@RestController
@RequestMapping("/testApi")
@Api(tags = "测试Api")
@RequiredArgsConstructor
public class TestApiController {
	private final ApiServiceImpl apiService;
	private final TestItemMapper testItemMapper;
	private final TestAsyncService testAsyncService;
	private final TestTxClass testTxClass;

	@Logger(back = true)
	@SqlLogger(mappers = TestMapper.class)
	@PostMapping("/testSUtils")
	@ApiOperation("测试SUtils方式")
	public Result<List<Test>> testSUtils(@RequestParam Long id) {
		List<Test> tests = apiService.listBy(SUtils.query(Test.class)
				.eq(Test::getId, id).show(Test::getId, Test::getName));
		return Result.ok(tests);
	}

	@PostMapping("/getError")
	@ApiOperation("测试getError")
	public Result<?> getError(@RequestHeader String language, @RequestParam String msg) {
		return ErrorUtil.getError(msg);
	}

	@PostMapping("/testInNull")
	@ApiOperation("测试空集合读取")
	public Result<List<Test>> testInNull() {
		List<Test> tests = apiService.listBy(SUtils.query(Test.class).in(Test::getId, null));
		return Result.ok(tests);
	}

	@PostMapping("/testInEmpty")
	@ApiOperation("测试空集合读取")
	public Result<List<Test>> testInEmpty() {
		List<Long> ids = new ArrayList<>();
		List<Test> tests = apiService.listBy(SUtils.query(Test.class).in(Test::getId, ids));
		return Result.ok(tests);
	}

	@PostMapping("/testCascadeRead")
	@ApiOperation("测试级联读取")
	public Result<Test> testCascadeRead(@RequestParam Long id) {
		Test test = apiService.findLast(SUtils.query(Test.class).id(id).showDefault()
				.cascade(Test::getItemList, SUtils.cascade(TestItem.class).show(TestItem::getName))
				.cascade(Test::getTestInfo, SUtils.cascade(TestInfo.class))
		);
		return Result.ok(test);
	}

	@PostMapping("/testCascadeSave")
	@ApiOperation("测试级联保存")
	public Result<Boolean> testCascadeSave(@Valid @RequestBody Test test) {
		return Result.ok(apiService.saveOne(test));
	}

	@PermAuth("/wdft/conf:add")
	@PostMapping("/testPermAuth")
	@ApiOperation("测试权限限制注解")
	public Result<Test> testPermAuth(@RequestParam Long id) {
		return this.testCascadeRead(id);
	}

	@PostMapping("/testAndOr")
	@ApiOperation("测试Or链接查询")
	public Result<Test> testAndOr(@RequestParam Long id) {
		SQuery<Test> sQuery = SUtils.query(Test.class).id(id).andOr(
				sChild -> sChild.eq(Test::getName, 1)
						.eq(Test::getStatus, 2)
						.eq(Test::getVersion, 3)
						.apply(SType.EQ, Test::getVersion, Test::getStatus)
		);
		return Result.ok(apiService.findLast(sQuery));
	}

	@PostMapping("/testSaveBatchTx")
	@ApiOperation("测试批量保存事务")
	public Result<Boolean> testSaveBatchTx(@Valid @RequestBody Test test) {
		boolean ret = apiService.saveBatchTx(CollUtil.toList(test,
				Test.builder().name("new_1").testInfoId(1L).build()));
		return Result.ok(ret);
	}

	@PostMapping("/testSaveBatchTx/")
	@ApiOperation("测试事务保存")
	public boolean testSaveBatchTx() {
		return apiService.saveBatchTx(Arrays.asList(
					// 正常
					Test.builder().name("test").testInfoId(1L).build(),
					// 出错了,验证回滚 [test.test_info_id 不能为空]
					Test.builder().name("test").build()
				));
	}

	@PostMapping("/testSIn")
	@ApiOperation("测试SIn")
	public List<Test> testSIn(@RequestParam Long id) {
		return apiService.listBy(SUtils.query(Test.class).sIn(Test::getTestItemIds, id));
	}

	@PostMapping("/testRedisLock")
	@ApiOperation("测试Redis分布式锁")
	public Result<Test> testRedisLock(@RequestParam Long id) throws Throwable {
		return LockHolder.getLockHolder().lock("" + id, 10, () -> this.testCascadeRead(id));
	}

	@PostMapping("/testApply/")
	@ApiOperation("测试testApply")
	public List<Test> testApply() {
		return apiService.listBy(SUtils.query(Test.class).apply(SType.EQ, Test::getStatus, Test::getVersion));
	}

	@PostMapping("/testInSql/")
	@ApiOperation("测试testInSql")
	public List<Test> testInSql() {
		return apiService.listBy(SUtils.query(Test.class).inSql(TestInfo.class, Test::getId, TestInfo::getTestId,
				apply -> apply.eq(TestInfo::getStatus, 1)));
	}

	@PostMapping("/testJoin/")
	@ApiOperation("测试testJoin")
	public Page<Test> testJoin(@RequestParam JoinType joinType) {
		apiService.exists(SUtils.query(Test.class)
				.join(joinType, TestInfo.class, Test::getId, TestInfo::getTestId, a -> a.orderAsc(TestInfo::getId))
				.eq(Test::getVersion, 1));
		return apiService.pageBy(1, 10, SUtils.query(Test.class)
				.join(joinType, TestInfo.class, Test::getId, TestInfo::getTestId, a -> a.orderAsc(TestInfo::getId))
				// apply -> apply.eq(TestInfo::getStatus, 1)
				.select(Test::getId, Test::getName)
				.eq(Test::getVersion, 1)
				.orderDesc(Test::getId)
		);
	}

	@PostMapping("/testUpdateBy/")
	@ApiOperation("测试updateBy")
	public boolean testUpdateBy() {
		// return apiService.updateBy(SUtils.update(Test.class).id(req.getId()).up(Test::getStatus, 1));
		return apiService.updateBy(SUtils.update(Test.class).in(Test::getId, Arrays.asList(34, 35)).set(Test::getStatus, 1));
	}

	@PostMapping("/testUpdateBatch/")
	@ApiOperation("测试updateBatch")
	public boolean testUpdateBatch() {
		// return apiService.updateBy(new SUpdate<Test>(){}.id(req.getId()).up(Test::getStatus, 1));
		return apiService.updateBatch(CollUtil.toList(
				SUtils.update(Test.class).in(Test::getId, Arrays.asList(31, 32)).set(Test::getStatus, 0),
				SUtils.update(TestInfo.class).eq(TestInfo::getId, 1).set(TestInfo::getStatus, 0)));
	}

	@Transactional
	@PostMapping("/testTransactional/")
	@ApiOperation("测试Transactional和异步")
	public boolean testTransactional(@RequestParam Long id, @RequestParam Integer status) {
		boolean ret = apiService.updateBy(SUtils.update(Test.class).id(id).set(Test::getStatus, status));
		// 注册事务提交后的同步器
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
			@Override
			public void afterCommit() {
				testAsyncService.printTest(id);
			}
		});
		if (status == 3) {
			throw new RuntimeException("测试事务");
		}
		return ret;
	}

	@PostMapping("/innerTransactional/")
	@ApiOperation("测试接口中的函数")
	public boolean innerTransactional(@RequestParam Long id, @RequestParam Integer status) {
		return testTxClass.innerTransactional(id, status, status == 3);
	}

	// 为了内部事务新起类
	@Service
	@RequiredArgsConstructor
	static class TestTxClass {
		private final ApiServiceImpl apiService;

		@Transactional
		public boolean innerTransactional(Long id, Integer status, boolean exception) {
			boolean ret = apiService.updateBy(SUtils.update(Test.class).id(id).set(Test::getStatus, status));
			// 注册事务提交后的同步器
			TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
				@Override
				public void afterCommit() {
					System.out.println("id=" + id);
				}
			});
			if (exception) {
				throw new RuntimeException("测试事务");
			}
			return ret;
		}
	}

	@DSTransactional
	@PostMapping("/testDSTransactional/")
	@ApiOperation("测试DSTransactional和异步")
	public boolean testDSTransactional(@RequestParam Long id, @RequestParam Integer status) {
		boolean ret = apiService.updateBy(SUtils.update(Test.class).id(id).set(Test::getStatus, status));
		// 注册事务提交后的同步器
		TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
			@Override
			public void afterCommit() {
				testAsyncService.printTest(id);
			}
		});
		return ret;
	}

	@PostMapping("/testListBy/")
	@ApiOperation("测试listBy")
	public List<Test> testListBy() {
		return apiService.listBy(SUtils.query(Test.class));
	}

	@PostMapping("/testPageBy/")
	@ApiOperation("测试pageBy")
	public Page<Test> testPageBy(@RequestBody @Valid PageReq req) {
		return apiService.pageBy(req.getPageNum(), req.getPageSize(), SUtils.query(Test.class));
	}

	@PostMapping("/testOffsetBy/")
	@ApiOperation("测试offsetBy")
	public List<Test> testOffsetBy(@RequestBody @Valid OffsetReq req) {
		return apiService.offsetBy(req.getPageSize(), req.getNextId(), SQuery.build(Test.class).orderDesc(Test::getId));
	}

	@PostMapping("/testSumBy/")
	@ApiOperation("测试sumBy")
	public BigDecimal testSumBy() {
		return apiService.sumBy(SUtils.query(Test.class).sumFields(Test::getVersion));
	}

	@PostMapping("/testGroupSumBy/")
	@ApiOperation("测试groupSumBy")
	public Result<List<Test>> testGroupSumBy() {
		Page<Test> page = apiService.groupSumBy(1, 1, SUtils.query(Test.class)
				.sumFields(Test::getId, Test::getVersion).groupBy(Test::getStatus).orderDesc(Test::getId));
		return Result.ok(page);
	}

	@PostMapping("/testCountBy/")
	@ApiOperation("测试countBy")
	public long testCount() {
		return apiService.countBy(SUtils.query(Test.class).eq(Test::getVersion, 1));
	}

	@PostMapping("/testExists/")
	@ApiOperation("测试exists")
	public boolean testExists() {
		return apiService.exists(SUtils.query(Test.class).eq(Test::getVersion, 1));
	}

	@PostMapping("/testMapperSql/")
	@ApiOperation("测试Mapper写SQL语句")
	public TestItem testMapperSql(@RequestBody @Valid DetailReq req) {
		return testItemMapper.selectById(req.getId());
	}

	@PostMapping("/testValid/")
	@ApiOperation("测试参数校验")
	public Parent testValid(@Valid @RequestBody Parent parent) {
		return parent;
	}

	@Data
	@NoArgsConstructor
	@AllArgsConstructor
	public static class Parent {
		@ApiModelProperty(value = "名字")
		@Size(min = 1, max = 3, message = "长度不符合")
		@NotBlank(message = "name 不能为空")
		private String name;

//		@Pattern(regexp = "(^Man$|^Woman$|^UGM$)", message = "sex 值不在可选范围")
//		@NotNull(message = "sex 不能为空")
//		private String sex;

		@ApiModelProperty(value = "年龄")
		@Min(value = 1, message = "最小值为1")
		@Max(value = 3, message = "最大为3")
		@NotNull(message = "age 不能为空")
		private Integer age;

		@EnumValid(target = StatusEnum.class, message = "状态值错误")
		private Integer status;

		@Valid
		@ApiModelProperty(value = "子集合")
//		@NotNull(message = "childList 不能为空")
		private List<Child> childList;
	}

	@Data
	@NoArgsConstructor
	@AllArgsConstructor
	public static class Child {
		@ApiModelProperty(value = "名字")
		@Size(min = 1, max = 3, message = "长度不符合")
		@NotNull(message = "name 不能为空")
		private String name;

		@ApiModelProperty(value = "年龄")
		@Min(value = 1, message = "最小值为1")
		@Max(value = 3, message = "最大为3")
		@NotNull(message = "age 不能为空")
		private Integer age;
	}
}